home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / MCC Utils / MCSimpleLists.p < prev    next >
Encoding:
Text File  |  1994-05-04  |  30.4 KB  |  909 lines  |  [TEXT/PJMM]

  1. {This document is formated in monaco 9 pt                                          }
  2. {                                                                                  }
  3. {LEGAL STUFF                                                                       }
  4. {                                                                                  }
  5. {Portions Copyright © 1994 by University of Melbourne. All Rights Reserved.        }
  6. {This work is provided "as is" and without any express or implied warranties,      }
  7. {including, without limitation, the implied warranties of merchantability and      }
  8. {fitness for a particular purpose.                                                 }
  9. {                                                                                  }
  10. {University of Melbourne is not responsible for the consequences of the use of this}
  11. {work, regardless of the cause. You may use this work in a public domain,          }
  12. {freeware, or shareware product with no restrictions, as long as you include       }
  13. {the following notice in your product's about box or splash screen:                }
  14. {  "Portions Copyright © 1994 by University of Melbourne".                         }
  15. {If you use more than 50 lines of this work, please credit the author(s) also:     }
  16. {  "Portions by Sean J. Crist and Michael Cutter"                                  }
  17. {Public domain is defined as something that you release to the public, without     }
  18. {copyright and without restrictions on use. Freeware is a copyrighted work,        }
  19. {for which you charge no money. Shareware is a copyrighted work for which you      }
  20. {charge a fee if the user decides to keep it. If you intend to use this work       }
  21. {in a commercial product, please contact us.                                       }
  22. {                                                                                  }
  23. {                                                                                  }
  24. {OTHER STUFF                                                                       }
  25. {                                                                                  }
  26. {Authors:                                                                          }
  27. { Sean J. Crist                                                                    }
  28. { Michael Trevor Cutter                                                            }
  29. {                                                                                  }
  30. {Contact:                                                                          }
  31. {  Internet:                                                                       }
  32. {    mtc@arbld.unimelb.edu.au (Preferred)                                          }
  33. {    kurisuto@bach.udel.edu                                                        }
  34. {                                                                                  }
  35. {  Snail Mail:                                                                     }
  36. {    Dept of Architecture & Building                                               }
  37. {    University of Melbourne                                                       }
  38. {    Parkville VIC 3052                                                            }
  39. {    AUSTRALIA                                                                     }
  40. {                                                                                  }
  41. {PERSONAL STUFF:                                                                   }
  42. {  I'd really appreciate it if you'd let me know what you're using my code         }
  43. {  in, (send me email or a postcard). Please report any bugs or errors to me.      }
  44. {                                                                                  }
  45. {MODULE DESCRIPTION:                                                               }
  46. {These procedures are built upon the work or Sean J. Crist. His comments are below }
  47. {I have modified most of his procedures, and added many of my own. I have labelled }
  48. {those which I wrote myself, and documented the changes I have made. I have also   }
  49. {renamed them for my own convenience in order to distinguish them in my code.      }
  50. {Sean, I hope you don't mind. I have included the original information he wrote    }
  51. {below, however check out the interface listing for up to date data.               }
  52. {The essential changes are adding facilities to access cells by index and allowing }
  53. {for multiple (or no) selections.                                                  }
  54.  
  55. {From: kurisuto@BACH.UDEL.EDU ("Sean J. Crist") }
  56. {Subject: More free code: Simplifying the List Manager }
  57. {Date: 18 Sep 92 03:33:13 GMT }
  58. {}
  59. {     The following code, once again, is nothing particularly glamorous; it }
  60. {simply makes it easier to use the List Manager to create and manage lists}
  61. {of strings.  One of the most common uses for the List Manager is}
  62. {scrollable, one-dimensional, fixed-size}
  63. {lists of strings (as in SFGetFile, SFPutFile).  The List Manager is good}
  64. {for creating all kinds of lists (such as lists of icons), but a lot of}
  65. {this functionality is a hassle for programmers who only need a simple list}
  66. {of strings.}
  67. {     The code below allows you create and dispose of lists of strings.  It}
  68. {allows you to add, rename, and remove elements in the list, and handles}
  69. {mouse clicks and update events.  It also keeps the lists in alphabetical}
  70. {order.}
  71. {     I remember having a lot of trouble learning how to call the List}
  72. {Manager properly; I hope that this code helps somebody else.}
  73. {}
  74. {How to use these routines:}
  75. {     Every one of these routines takes a ListHandle as one of its}
  76. {parameters.  It is OK to have several lists going at one time; just make}
  77. {sure you pass the right ListHandle when you call one of these routines.}
  78. {     }
  79. {CreateList:  Call this routine to create a new list of strings.  Pass it}
  80. {an empty ListHandle, as well as the pointer to the window to contain the}
  81. {list, and the rectangle in which the list should be enclosed.  The list}
  82. {can take up an entire window, or only}
  83. {part of it, as you prefer.  The scroll bar will be drawn inside the}
  84. {rectangle you specify, so you don't need to leave extra room for it. }
  85. {Initially, the list will contain no strings.}
  86. {}
  87. {UpdateList:  Call this in response to an Update event to redraw the}
  88. {portions of the list which need it.  This routine assumes that you have}
  89. {already called BeginUpdate for the window containing the list, and that}
  90. {you have not yet called EndUpdate.}
  91. {}
  92. {DoClick:  Call this function in response to a MouseDown event inside your}
  93. {list's rectangle.  DoClick usually returns FALSE, but it returns TRUE if}
  94. {this click is the second click of a double-click (i.e., TRUE means the}
  95. {user double-clicked an item).}
  96. {}
  97. {TurnOffSelection:  This routine unhilights the currently hilighted cell,}
  98. {if any.}
  99. {}
  100. {ListSelection:  This returns the string of the currently selected cell. }
  101. {If no cell is currently selected, the empty string is returned.}
  102. {}
  103. {AddCell:  Call this routine to add a new cell to the list.  The string you}
  104. {pass to this routine is automatically alphabetized within the list.  Bug}
  105. {alert:  the first element or two of the list are sometimes not in}
  106. {alphabetical order.  If you take the time}
  107. {to work out this kink, please let me know what you did (I could figure}
  108. {this out, but I just haven't taken the time to).  This bug is cosmetic and}
  109. {does not crash the program.}
  110. {}
  111. {RenameCell:  Changes the string in a cell to another string, and}
  112. {realphabetizes the list, if necessary.  }
  113. {}
  114. {DeleteCell:  Removes the cell with the given string from the list.}
  115. {}
  116. {DisposList:  Call this routine when you are completely finished with a}
  117. {list and want to deallocate its memory.}
  118. {}
  119. {     Credit: The routines CreateList, UpdateList, DoClick, and DisposList}
  120. {are loosely based on some code I received from somebody of}
  121. {comp.sys.mac.programmer around two years ago.  I've rewritten these}
  122. {routines, but credit is due to this contributor, whose}
  123. {name I cannot remember.}
  124. {}
  125. {     I have successfully called these routines for lists in regular}
  126. {windows as well as in modal dialogs.  Please send bug reports, praise,}
  127. {money, etc. to kurisuto@bach.udel.edu.}
  128. {}
  129. unit MCSimpleLists;
  130.  
  131. interface
  132.     uses
  133.         MCCursor; {only in the MCSortList function}
  134.  
  135.     const
  136.         StandardListLDEF = 0;
  137.         kMCSLScrollBarWidth = 16;
  138. {width of the scrollbar as created by MCCreateList}
  139.  
  140.     type
  141.     {CellIndex is provided as a reminder that when passing in a value of}
  142.     {this type, it refers to the cell index, which starts at 1, not 0.}
  143.         CellIndex = integer;
  144.     {Explanation: CellIndex calls assume that the cells are numbered 1..n.}
  145.     {The Cell.v variable contains index - 1, i.e numbers the cells from 0..n-1}
  146.     {I did this because I am a pascal diehard}
  147.  
  148. {Author: Sean J. Crist}
  149. {Modified by Mike Cutter to include option for specifying LDEF and cell height.}
  150. {Create a new list with no elements.}
  151. {listh should be nil, thewindow <> nil, LDEFID can be StandardListLDEF if you}
  152. {just want a standard list, or the resid of the LDEF if you don't, cellHeight}
  153. {is calculated from the text height if you set it to 0, and obviously theRect}
  154. {is where to put it in local coordinates}
  155.     procedure MCCreateList (var listh: ListHandle;
  156.                                     TheWindow: WindowPtr;
  157.                                     LDEFID: integer;
  158.                                     cellHeight: integer;
  159.                                     TheRect: Rect);
  160.  
  161. {Author: Sean J. Crist}
  162. {Modified by Mike Cutter to detect nil list}
  163. {Update the art of a list. Call this in your update routine, or put it into}
  164. {a drawing routine for your useritem in a dialog (you can store the list into}
  165. {the refcon of the dialog)}
  166.     procedure MCUpdateList (listh: ListHandle);
  167.  
  168. {Author: Sean J. Crist}
  169. {Modified by Mike Cutter to detect clicks in non-existant cells}
  170. {Handle a click in the list rectangle.  If it was a double-click, we will}
  171. {return TRUE. Pass 0 in modifiers if you just want a single selection list,}
  172. {event.modifiers if you want multiple selection etc.}
  173. {if you want to be sure you clicked in an actual existing cell (and not in an}
  174. {empty space, check goodClick. Pass in goodClick = true to get it to check, if you}
  175. {don't care, goodClick = false}
  176.     function MCDoClickList (listh: ListHandle;
  177.                                     TheWhere: Point;
  178.                                     Modifiers: integer;
  179.                                     var goodClick: Boolean): Boolean;
  180.  
  181. {Author: Michael Cutter}
  182. {Get the index of the last cell clicked in. Don't forget, this won't have much bearing}
  183. {if the user has dragged across several cells. But if they double-clicked, that will tell}
  184. {us which cell it was that they doubleclicked}
  185.     function MCGetLastIndexedClicked (listh: ListHandle): CellIndex;
  186.  
  187. {Author: Michael Cutter}
  188. {Enable and disable drawing of the list}
  189.     procedure MCNoListDraw (listh: ListHandle);
  190.     procedure MCYesListDraw (listh: ListHandle);
  191.  
  192. {Author: Sean J. Crist}
  193. {Modified by Mike Cutter to turn off multiple selections, rather than just the first}
  194. {Turn off any hilited item.}
  195.     procedure MCTurnOffListSelection (listh: ListHandle);
  196.  
  197. {Author: Mike Cutter}
  198. {Get the visible rectangle of the list in local coordinates}
  199.     procedure MCGetListViewRect (list: ListHandle;
  200.                                     var lrect: Rect);
  201.  
  202. {Author: Mike Cutter}
  203. {Check whether a specified item is in the list}
  204.     function MCCheckForCell (listh: ListHandle;
  205.                                     TheStr: string): Boolean;
  206.  
  207. {Author: Mike Cutter}
  208. {Select the first item in the list, useful when a list has just been created}
  209.     procedure MCSelectFirstCell (listh: ListHandle;
  210.                                     oneselection: Boolean);
  211.  
  212. {Author: Mike Cutter}
  213. {Select an item specified by its contents. Set oneselection = true if you only}
  214. {ever want one item to be selected, otherwise set it to false if you want multiple}
  215. {selections permitted.}
  216.     procedure MCSelectCell (listh: ListHandle;
  217.                                     TheStr: string;
  218.                                     oneselection: Boolean);
  219.  
  220. {Author: Sean J. Crist}
  221. {Modified by Mike Cutter to detect nil listh}
  222. {Returns the single string currently selected; empty string if no selection . }
  223.     function MCGetListSelection (listh: ListHandle): string;
  224.  
  225. {Author: Mike Cutter}
  226. {Used to retrieve all the selected cells, one after the other.}
  227. {Set last to 0 first, increment it after calling the function to avoid}
  228. {finding same cell again. Returns -1 in last when at the end of the selected cells}
  229.     function MCGetNextListSelection (listh: ListHandle;
  230.                                     var last: CellIndex): Str255;
  231.  
  232. {Author: Mike Cutter}
  233. {Returns the number of selected items in the list}
  234.     function MCCountListSelected (listh: ListHandle): integer;
  235.  
  236. {Author: Sean J Crist}
  237. {Modified by Mike Cutter to leave sorting the list till after it has been created.}
  238. {Add a new cell to the end of the list}
  239.     procedure MCAddCell (listh: ListHandle;
  240.                                     NewStr: str255);
  241.  
  242. {Author: Mike Cutter}
  243. {Count the number of cells}
  244.     function MCCountListCells (listh: ListHandle): integer;
  245.  
  246. {Author: Mike Cutter}
  247. {Select the cell specified by the index}
  248.     procedure MCSelectIndexedCell (listh: ListHandle;
  249.                                     index: CellIndex;
  250.                                     oneselection: Boolean);
  251.  
  252. {Author: Mike Cutter}
  253. {Get the contents of the index'th cell}
  254.     function MCGetIndexedListCell (listh: ListHandle;
  255.                                     index: CellIndex): Str255;
  256.  
  257. {Author: Mike Cutter}
  258. {Get the index of the named cell}
  259.     function MCGetCellIndex (listh: ListHandle;
  260.                                     TheStr: Str255): CellIndex;
  261.  
  262. {Author: Mike Cutter}
  263. {Set the name of the index'th cell}
  264.     procedure MCSetIndexedListCell (listh: ListHandle;
  265.                                     index: CellIndex;
  266.                                     NewStr: string);
  267.  
  268. {Author: Mike Cutter}
  269. {Sort the list alphabetically}
  270.     procedure MCSortList (listh: ListHandle);
  271. {note this doesn't require an index, but it uses indexed functions}
  272.  
  273. {Author: Sean J. Crist}
  274. {Modified by Mike Cutter to detect nil list}
  275. {Change the name of an existing cell}
  276.     procedure MCRenameCell (listh: ListHandle;
  277.                                     OldStr, NewStr: Str255);
  278.  
  279. {Author: Sean J. Crist}
  280. {Modified by Mike Cutter to detect a nil list}
  281. {Remove the cell with the given name from the list.}
  282.     procedure MCDeleteCell (listh: ListHandle;
  283.                                     TheStr: string);
  284.  
  285. {Author: Mike Cutter}
  286. {Remove the indexed cell from the list}
  287.     procedure MCDeleteIndexedCell (listh: ListHandle;
  288.                                     index: CellIndex);
  289.  
  290. {Author Sean J. Crist}
  291. {Modified by Mike Cutter to detect a nil list}
  292. {Get rid of the list when we're done with it, cleaning up all the memory.}
  293.     procedure MCDisposeList (listh: ListHandle);
  294.  
  295. implementation
  296.     var
  297.         gMCSLCellHeight: integer; {for remembering the cellheight for MCDoClick}
  298.  
  299.     procedure MCCreateList (var listh: ListHandle;
  300.                                     TheWindow: WindowPtr;
  301.                                     LDEFID: integer;
  302.                                     cellHeight: integer;
  303.                                     TheRect: Rect);
  304.         var
  305.             ViewRect: Rect;
  306.             DataBounds: Rect;
  307.             CellSize: Point;
  308.             TempInteger: Integer;  {Just to do a little math}
  309.     begin
  310.         if listh <> nil then
  311.             begin
  312.                 DebugStr('list handle not initialized to nil in MCCreateList');
  313.             end;
  314. {Inset the box to make room for the scroll bar.  Also inset it so we've got room for a border.}
  315.         ViewRect := TheRect;
  316.         InsetRect(ViewRect, 1, 1);
  317.         ViewRect.Right := ViewRect.Right - 15;
  318. {Set the cell size to the size of the cell}
  319.         if cellHeight = 0 then
  320.         {we don't care what the cellheight is, so work it out from the window text font}
  321.             begin
  322.                 CellSize.v := TheWindow^.txSize + 3;
  323.                 if CellSize.v = 3 then  {If it hasn't been set, then make it 12 point.}
  324.                     begin
  325.                         TextSize(12);
  326.                         CellSize.v := 15;
  327.                     end;
  328.             end
  329.         else
  330.             CellSize.v := cellHeight;
  331.         gMCSLCellHeight := CellSize.v;
  332. {set the width}
  333.         CellSize.h := ViewRect.Right - ViewRect.Left;
  334.  
  335. {Now adjust the ViewRect to avoid cutting off the last visible cell}
  336.         TempInteger := (ViewRect.Bottom - ViewRect.Top) div CellSize.v;
  337.         ViewRect.Bottom := ViewRect.Top + (TempInteger * CellSize.v);
  338.  
  339. { and create the new list.}
  340.         SetRect(DataBounds, 0, 0, 1, 0);
  341.         listh := LNew(ViewRect, DataBounds, CellSize, LDEFID, TheWindow, FALSE, FALSE, FALSE, TRUE);
  342.         MCUpdateList(listh);
  343.     end;
  344.  
  345. {Update the display of a list.}
  346.     procedure MCUpdateList;
  347.         var
  348.             ViewRect: Rect;
  349.             ListUpdateRgn: RgnHandle;
  350.     begin
  351.         if listh = nil then
  352.             begin
  353.                 DebugStr('list handle is nil in MCUpdateList');
  354.                 exit(MCUpdateList);
  355.             end;
  356.         SetPort(listh^^.Port);
  357. {Get the List manager to update the list.}
  358.         ViewRect := listh^^.rView;
  359.         EraseRect(ViewRect); {erase the rect}
  360.         LDoDraw(true, listh);
  361.         ListUpdateRgn := NewRgn;
  362.         RectRgn(ListUpdateRgn, ViewRect);
  363.         LUpdate(ListUpdateRgn, listh);
  364. {Draw the border}
  365.         InsetRect(ViewRect, -1, -1);
  366.         FrameRect(ViewRect);
  367. {Clean up after ourselves}
  368.         DisposeRgn(ListUpdateRgn);
  369.     end;
  370.  
  371. {Handle a click in the list rectangle.  If it was a double-click, we will return TRUE.}
  372. {trick to avoiding bad clicks, is to check that the click is in the rect defined}
  373. {by the number of items in the list, _IF_ the list count * the cellheight is smaller}
  374. {than the rect of the list.}
  375. {Note that we aren't passing in the modifiers with the click, thus forcing this list}
  376. {to only ever behave as a single selection list}
  377. {To allow multiple selections, there needs to be a way of getting each selection in order}
  378. {and clicks must be sent to a different (but similar) function which receives and passes}
  379. {on the event.modifiers}
  380.     function MCDoClickList;
  381.         var
  382.             cellsRect, viewRect: Rect;
  383.     begin
  384.         if listh = nil then
  385.             begin
  386.                 DebugStr('list handle is nil in MCDoClickList');
  387.                 exit(MCDoClickList);
  388.             end;
  389.         SetPort(listh^^.Port);
  390.         cellsRect := listh^^.rView;
  391. {adjust to include the scroll bar}
  392.         cellsRect.right := cellsRect.right + kMCSLScrollBarWidth;
  393.         viewRect := cellsRect; {remember the visible rectangle}
  394. {and adjust the cellsrect to be the size of all the existing cells - which}
  395. {may or may not be bigger than the visible rect}
  396.         cellsRect.bottom := cellsRect.top + (listh^^.cellSize.v * MCCountListCells(listh)); {}
  397.         if goodClick then
  398.             if PtInRect(TheWhere, viewRect) then {got a click in either the scrollbar or the list}
  399.                 if PtInRect(theWhere, cellsRect) then {got a click inside the cells}
  400.                     begin
  401.                         LDoDraw(TRUE, listh);
  402.                         MCDoClickList := LClick(TheWhere, modifiers, listh);
  403.                         goodClick := true;
  404.                     end
  405.                 else
  406.                     begin
  407.                         viewRect.left := viewRect.right - kMCSLScrollBarWidth;
  408.                         if not PtInRect(theWhere, viewRect) then {check it wasn't a click in the sb}
  409.                             begin
  410.                                 SysBeep(1);
  411.                             end;
  412.                         goodClick := false; {wasn't a click on a list item}
  413.                     end
  414.             else {did't click in the list, idiot!}
  415.         else
  416.             begin
  417.                 LDoDraw(true, listh);
  418.                 MCDoClickList := LClick(TheWhere, modifiers, listh);
  419.             end;
  420.     end;
  421.  
  422.     function MCGetLastIndexedClicked (listh: ListHandle): CellIndex;
  423.         var
  424.             cellpt: Cell;
  425.     begin
  426.         if listh = nil then
  427.             begin
  428.                 DebugStr('list handle is nil in MCGetLastIndexedClicked');
  429.                 MCGetLastIndexedClicked := 0;
  430.                 exit(MCGetLastIndexedClicked);
  431.             end;
  432.         SetPt(cellpt, 0, 0);
  433.         cellpt := LLastClick(listh);
  434.         MCGetLastIndexedClicked := cellpt.v + 1; {because indexes go from 1..cellcount}
  435.     end;
  436.  
  437.     procedure MCNoListDraw (listh: ListHandle);
  438.     begin
  439.         if listh = nil then
  440.             begin
  441.                 DebugStr('list handle is nil in MCNoListDraw');
  442.                 exit(MCNoListDraw);
  443.             end;
  444.         LDoDraw(false, listh);
  445.     end;
  446.  
  447.     procedure MCYesListDraw (listh: ListHandle);
  448.     begin
  449.         if listh = nil then
  450.             begin
  451.                 DebugStr('list handle is nil in MCYesListDraw');
  452.                 exit(MCYesListDraw);
  453.             end;
  454.         LDoDraw(true, listh);
  455.     end;
  456.  
  457. {Turn off all hilited items.}
  458.     procedure MCTurnOffListSelection;
  459.         var
  460.             ResultPoint: Cell;
  461.     begin
  462.         if listh = nil then
  463.             begin
  464.                 DebugStr('list handle is nil in MCTurnOffListSelection');
  465.                 exit(MCTurnOffListSelection);
  466.             end;
  467.         SetPt(ResultPoint, 0, 0);
  468.         while LGetSelect(TRUE, ResultPoint, listh) do
  469.             begin
  470.                 LSetSelect(FALSE, ResultPoint, listh);
  471.                 SetPt(ResultPoint, 0, 0);
  472.             end;
  473.     end;
  474.  
  475. {Get the visible rectangle of the list}
  476.     procedure MCGetListViewRect (list: ListHandle;
  477.                                     var lrect: Rect);
  478.     begin
  479.         if list = nil then
  480.             begin
  481.                 DebugStr('list handle is nil in MCGetListViewRect');
  482.                 exit(MCGetListViewRect);
  483.             end;
  484.         lrect := list^^.rView;
  485.     end;
  486.  
  487. {Check the existence of a particular cell}
  488.     function MCCheckForCell;
  489.         var
  490.             CellPoint: Cell;
  491.             DataPtr: Ptr;
  492.             DataLen: Integer;
  493.     begin
  494.         if listh = nil then
  495.             begin
  496.                 DebugStr('list handle is nil in MCCheckForCell');
  497.                 MCCheckForCell := false;
  498.                 exit(MCCheckForCell);
  499.             end;
  500.         SetPt(CellPoint, 0, 0);
  501.         DataPtr := Pointer(Ord(@TheStr) + 1);
  502.         dataLen := Length(TheStr);
  503.         MCCheckForCell := LSearch(dataPtr, dataLen, nil, CellPoint, listh);
  504.     end;
  505.  
  506. {Select the first item}
  507.     procedure MCSelectFirstCell;
  508. {By Michael Cutter, Sept 93}
  509.         var
  510.             CellPoint: Cell;
  511.     begin
  512.         if listh = nil then
  513.             begin
  514.                 DebugStr('list handle is nil in MCSelectFirstCell');
  515.                 exit(MCSelectFirstCell);
  516.             end;
  517.         if MCCountListCells(listh) > 0 then
  518.             begin
  519.                 if oneselection then
  520.                     begin
  521.                         SetPt(CellPoint, 0, 0);
  522.                         while LGetSelect(TRUE, CellPoint, listh) do
  523.                             begin
  524.                                 LSetSelect(FALSE, CellPoint, listh);
  525.                                 SetPt(CellPoint, 0, 0);
  526.                             end;
  527.                     end;
  528.                 SetPt(CellPoint, 0, 0);
  529.                 LSetSelect(TRUE, CellPoint, listh);
  530.             end;
  531.     end;
  532.  
  533. {Select a particular item}
  534.     procedure MCSelectCell;
  535. {By Michael Cutter, Sept 93}
  536.         var
  537.             OldPoint, CellPoint: Cell;
  538.             DataPtr: Ptr;
  539.             DataLen: Integer;
  540.     begin
  541.         if listh = nil then
  542.             begin
  543.                 DebugStr('list handle is nil in MCSelectCell');
  544.                 exit(MCSelectCell);
  545.             end;
  546.         if MCCountListCells(listh) > 0 then
  547.             begin
  548.                 SetPt(CellPoint, 0, 0);
  549.                 DataPtr := Pointer(Ord(@TheStr) + 1);
  550.                 dataLen := Length(TheStr);
  551.                 if LSearch(dataPtr, dataLen, nil, CellPoint, listh) then
  552.                     begin
  553.                         if oneselection then
  554.                             begin {deselect any current selection}
  555.                                 SetPt(OldPoint, 0, 0);
  556.                                 while LGetSelect(TRUE, OldPoint, listh) do
  557.                                     begin
  558.                                         LSetSelect(FALSE, OldPoint, listh);
  559.                                         SetPt(OldPoint, 0, 0);
  560.                                     end;
  561.                             end;
  562.             {select the cell we want}
  563.                         LSetSelect(TRUE, CellPoint, listh);
  564.                     end
  565.                 else
  566.                     begin
  567. {The programmer asked us to select a cell which doesn't exist.  We'll just beep angrily }
  568. {three times.  It's the programmer's responsibility to see that the cell in question actually}
  569. {does exist before calling this routine.}
  570.                         Sysbeep(1);
  571.                         Sysbeep(1);
  572.                         Sysbeep(1);
  573.                     end;
  574.             end;
  575.     end;
  576.  
  577. {Return the string currently selected; empty string if no selection.}
  578.     function MCGetListSelection;
  579.         var
  580.             ResultPoint: Cell;
  581.             ResultString: Str255;
  582.             StringPointer: Ptr;
  583.             StringLength: Integer;
  584.     begin
  585.         MCGetListSelection := '';
  586.         if listh = nil then
  587.             begin
  588.                 DebugStr('list handle is nil in MCGetListSelection');
  589.                 exit(MCGetListSelection);
  590.             end;
  591.         SetPt(ResultPoint, 0, 0);
  592.         if LGetSelect(TRUE, ResultPoint, listh) then
  593. {If there is a cell selected, then get the string value of that string. }
  594. {There ought to be an easier way to do this than mucking around in the }
  595. {memory like this.  >:-( }
  596.             begin  {If there is a cell selected, then return the string of the cell.}
  597.                 StringPointer := Ptr(Ord(@ResultString) + 1);
  598.                 StringLength := 255;  {This is the maximum amount of data we are allowed to move.}
  599.                 LGetCell(StringPointer, StringLength, ResultPoint, listh);
  600.                 StringPointer := Ptr(Ord(@ResultString));
  601.                 StringPointer^ := StringLength;
  602.                 MCGetListSelection := ResultString;
  603.             end; {Otherwise, return the empty string to show that nothing is selected.}
  604.     end;
  605.  
  606.     function MCGetNextListSelection (listh: ListHandle;
  607.                                     var last: CellIndex): Str255;
  608.         var
  609.             ResultPoint: Cell;
  610.             stringsize: integer;
  611.             str: Str255;
  612.     begin
  613.         str := '';
  614.         ResultPoint.h := 0;
  615.         ResultPoint.v := last;
  616.         if LGetSelect(TRUE, ResultPoint, listh) then
  617.             begin
  618.                 stringsize := sizeof(Str255);
  619.                 LGetCell(@str, stringsize, ResultPoint, listh);
  620.                 last := ResultPoint.v
  621.             end
  622.         else
  623.             last := -1; {no more selected items}
  624.         MCGetNextListSelection := str;
  625.     end;
  626.  
  627.     function MCCountListSelected (listh: ListHandle): integer;
  628.         var
  629.             CellPoint: Cell;
  630.             count: integer;
  631.     begin
  632.         if listh = nil then
  633.             begin
  634.                 DebugStr('list handle is nil in MCCountListSelected');
  635.                 MCCountListSelected := 0;
  636.                 exit(MCCountListSelected);
  637.             end;
  638.         count := 0;
  639.         SetPt(CellPoint, 0, 0);
  640.         while LGetSelect(TRUE, CellPoint, listh) do
  641.             begin
  642.                 count := count + 1;
  643.                 CellPoint.v := CellPoint.v + 1;
  644.             end;
  645.         MCCountListSelected := count;
  646.     end;
  647.  
  648.     procedure MCAddCell;
  649.         var
  650.             CellPoint: Cell;
  651.     begin
  652.         if listh = nil then
  653.             begin
  654.                 DebugStr('list handle is nil in MCAddCell');
  655.                 exit(MCAddCell);
  656.             end;
  657. {Add the new row at the top of the list.}
  658.         SetPt(CellPoint, 0, 0);
  659.         CellPoint.v := LAddRow(1, CellPoint.v, listh);
  660. {Put the string into the cell.  Once again, there ought to be an easier}
  661. {way to do this.}
  662.         LSetCell(Pointer(Ord(@NewStr) + 1), Length(NewStr), CellPoint, listh);
  663.     end;
  664.  
  665.     procedure MCSelectIndexedCell (listh: ListHandle;
  666.                                     index: CellIndex;
  667.                                     oneselection: Boolean);
  668.         var
  669.             OldPoint, CellPoint: Cell;
  670.     begin
  671.         if listh = nil then
  672.             begin
  673.                 DebugStr('list handle is nil in MCSelectIndexedCell');
  674.                 exit(MCSelectIndexedCell);
  675.             end;
  676.         CellPoint.h := 0;
  677.         CellPoint.v := index - 1;
  678.         if oneselection then
  679.             begin
  680.             {deselect any current selection}
  681.                 SetPt(OldPoint, 0, 0);
  682.                 while LGetSelect(TRUE, OldPoint, listh) do
  683.                     begin
  684.                         LSetSelect(FALSE, OldPoint, listh);
  685.                         SetPt(OldPoint, 0, 0);
  686.                     end;
  687.             end;{select the cell we want}
  688.         LSetSelect(TRUE, CellPoint, listh);
  689.     end;
  690.  
  691.     function MCGetIndexedListCell;
  692. {By Michael Cutter, Sept 1993}
  693.         var
  694.             CellPoint: Cell;
  695.             StringPointer: Ptr;
  696.             StringLength: Integer;
  697.             NewStr: Str255;
  698.     begin
  699.         if listh = nil then
  700.             begin
  701.                 DebugStr('list handle is nil in MCGetIndexedListCell');
  702.                 MCGetIndexedListCell := '';
  703.                 exit(MCGetIndexedListCell);
  704.             end;
  705.         NewStr := '';
  706.         if (index <= MCCountListCells(listh)) and (index > 0) then
  707.             begin
  708.                 SetPt(CellPoint, 0, index - 1);
  709.                 StringPointer := Ptr(Ord(@NewStr) + 1);
  710.                 StringLength := 255;
  711.                 LGetCell(StringPointer, StringLength, CellPoint, listh);
  712.                 StringPointer := Ptr(Ord(@NewStr));
  713.                 StringPointer^ := StringLength; {assign the length byte}
  714.             end;
  715.         MCGetIndexedListCell := NewStr;
  716.     end;
  717.  
  718.     function MCCountListCells;
  719. {By Michael Cutter, Sept 1993, revised Mar 1994}
  720.     begin
  721.         if listh = nil then
  722.             begin
  723.                 DebugStr('list handle is nil in MCCountListCells');
  724.                 MCCountListCells := 0;
  725.                 exit(MCCountListCells);
  726.             end;
  727.         with listh^^.dataBounds do
  728.             MCCountListCells := bottom * right; {if there is only one column, then right = 1}
  729.     end;
  730.  
  731.     function MCGetCellIndex (listh: ListHandle;
  732.                                     TheStr: Str255): CellIndex;
  733. {By Michael Cutter, March 94}
  734.         var
  735.             OldPoint, CellPoint: Cell;
  736.             DataPtr: Ptr;
  737.             DataLen: Integer;
  738.     begin
  739.         if listh = nil then
  740.             begin
  741.                 DebugStr('list handle is nil in MCGetCellIndex');
  742.                 MCGetCellIndex := 0;
  743.                 exit(MCGetCellIndex);
  744.             end;
  745.         SetPt(CellPoint, 0, 0);
  746.         DataPtr := Pointer(Ord(@TheStr) + 1);
  747.         dataLen := Length(TheStr);
  748.         if LSearch(dataPtr, dataLen, nil, CellPoint, listh) then
  749.             begin
  750. {return the index of the cell we wanted}
  751.                 MCGetCellIndex := CellPoint.v + 1;
  752.             end
  753.         else
  754.             MCGetCellIndex := 0;
  755.     end;
  756.  
  757.     procedure MCSetIndexedListCell (listh: ListHandle;
  758.                                     index: CellIndex;
  759.                                     NewStr: string);
  760.         var
  761.             CellPoint: Cell;
  762.             StringPointer: Ptr;
  763.     begin
  764.         if listh = nil then
  765.             begin
  766.                 DebugStr('list handle is nil in MCSetIndexedListCell');
  767.                 exit(MCSetIndexedListCell);
  768.             end;
  769.         if (index <= MCCountListCells(listh)) and (index > 0) then
  770.             begin
  771.                 SetPt(CellPoint, 0, index - 1);
  772.                 StringPointer := Ptr(Ord(@NewStr) + 1);
  773.                 LSetCell(StringPointer, length(NewStr), CellPoint, listh);
  774.             end;
  775.     end;
  776.  
  777.     procedure MCSortList (listh: ListHandle);
  778. {By Michael Cutter, Sept 1993}
  779.         var
  780.             itemcount, index, ind2: CellIndex;
  781.             curritem, previtem: str255;
  782.     begin
  783.         if listh = nil then
  784.             begin
  785.                 DebugStr('list handle is nil in MCSortList');
  786.                 exit(MCSortList);
  787.             end;
  788.         MCNoListDraw(listh);
  789.         itemcount := MCCountListCells(listh);
  790.         for index := 2 to itemcount do
  791.             begin
  792.                 MCNextAnimCursor;
  793.                 curritem := MCGetIndexedListCell(listh, index); {get the string of the index'th menu item}
  794.                 ind2 := index;
  795.                 previtem := MCGetIndexedListCell(listh, ind2 - 1); {get the string of the index-1'th menu item}
  796.                 while (RelString(curritem, previtem, false, false) < 0) and (ind2 <> 1) do {while curritem less than previtem}
  797.                     begin
  798.                         MCNextAnimCursor;
  799.                         MCSetIndexedListCell(listh, ind2, MCGetIndexedListCell(listh, ind2 - 1));
  800.                         ind2 := ind2 - 1;
  801.                         previtem := MCGetIndexedListCell(listh, ind2 - 1); {get the string of the index-1'th menu item}
  802.                     end;
  803.                 MCSetIndexedListCell(listh, ind2, curritem);
  804.             end;
  805.         MCYesListDraw(listh);
  806.     end;
  807.  
  808.     procedure MCRenameCell;
  809.         var
  810.             CellPoint: Cell;
  811.             DataPtr: Ptr;
  812.             DataLen: Integer;
  813.     begin
  814.         if listh = nil then
  815.             begin
  816.                 DebugStr('list handle is nil in MCRenameCell');
  817.                 exit(MCRenameCell);
  818.             end;
  819.         SetPt(CellPoint, 0, 0);
  820.         DataPtr := Pointer(Ord(@OldStr) + 1);
  821.         dataLen := Length(OldStr);
  822.         if LSearch(dataPtr, dataLen, nil, CellPoint, listh) then
  823.             begin
  824.                 DataPtr := Pointer(Ord(@NewStr) + 1);
  825.                 dataLen := Length(NewStr);
  826.                 LSetCell(DataPtr, dataLen, CellPoint, listh);
  827.             end
  828.         else
  829.             begin
  830. {The programmer asked us to rename a cell which doesn't exist.  We'll just}
  831. {beep angrily}
  832. {three times.  It's the programmer's responsibility to see that the cell}
  833. {in question actually}
  834. {does exist before calling this routine.}
  835.                 Sysbeep(1);
  836.                 Sysbeep(1);
  837.                 Sysbeep(1);
  838.             end;
  839.     end;
  840.  
  841.  
  842. {Remove the cell with the given name from the list.}
  843.     procedure MCDeleteCell;
  844.         var
  845.             CellPoint: Cell;
  846.             DataPtr: Ptr;
  847.             DataLen: Integer;
  848.     begin
  849.         if listh = nil then
  850.             begin
  851.                 DebugStr('list handle is nil in MCDeleteCell');
  852.                 exit(MCDeleteCell);
  853.             end;
  854.         SetPt(CellPoint, 0, 0);
  855.         DataPtr := Pointer(Ord(@TheStr) + 1);
  856.         dataLen := Length(TheStr);
  857.         if LSearch(dataPtr, dataLen, nil, CellPoint, listh) then
  858.             begin
  859.                 LDelRow(1, CellPoint.v, listh);
  860.             end
  861.         else
  862.             begin
  863. {The programmer asked us to delete a cell which doesn't exist.  We'll just}
  864. {beep angrily}
  865. {three times.  It's the programmer's responsibility to see that the cell}
  866. {in question actually}
  867. {does exist before calling this routine.}
  868.                 Sysbeep(1);
  869.                 Sysbeep(1);
  870.                 Sysbeep(1);
  871.             end;
  872.     end;
  873.  
  874. {Remove the indexed cell from the list.}
  875.     procedure MCDeleteIndexedCell;
  876. {By Mike Cutter, April 94}
  877.     begin
  878.         if listh = nil then
  879.             begin
  880.                 DebugStr('list handle is nil in MCDeleteIndexedCell');
  881.                 exit(MCDeleteIndexedCell);
  882.             end;
  883.         if index <= MCCountListCells(listh) then
  884.             begin
  885.                 LDelRow(1, index - 1, listh);
  886.             end
  887.         else
  888.             begin
  889. {The programmer asked us to delete a cell which doesn't exist.  We'll just beep angrily}
  890. {three times.  It's the programmer's responsibility to see that the cell}
  891. {in question actually does exist before calling this routine.}
  892.                 Sysbeep(1);
  893.                 Sysbeep(1);
  894.                 Sysbeep(1);
  895.             end;
  896.     end;
  897.  
  898. {Get rid of the list when we're done with it, cleaning up all the memory.}
  899.     procedure MCDisposeList;
  900.     begin
  901.         if listh = nil then
  902.             begin
  903.                 DebugStr('list handle is nil in MCDisposeList');
  904.                 exit(MCDisposeList);
  905.             end;
  906.         LDispose(listh);
  907.     end;
  908.  
  909. end.